package yns.time;

import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.HashMap;
import java.util.Map;

/**
 * 针对JDK8之后的JDK
 * 
 * @author YNS
 *
 */
public class TimeUtility {
	
	/**
	 * 补充一个JDK8时间类型对应数据库类型
	 * 
	 * date -> LocalDate
	 * 
	 * time -> LocalTime
	 * 
	 * timestamp -> LocalDateTime
	 * 
	 */

	public static final String FORMATTER_STANDARD = "yyyy-MM-dd HH:mm:ss";
	public static final String FORMATTER_LONG = "yyyy-MM-dd HH:mm:ss.SSS";
	public static final String FORMATTER_SHORT = "yyyy-MM-dd";
	public static ZoneId DEFAULT_ZONE = ZoneId.systemDefault();

	/**
	 * 
	 * 返回当前时间,格式：yyyy-MM-dd HH:mm:ss
	 * 
	 * @return
	 */
	public static String getCurrentTime() {
		LocalDateTime localDateTime = LocalDateTime.now();
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(FORMATTER_STANDARD);
		return localDateTime.format(dateTimeFormatter);
	}

	/**
	 * 
	 * 返回当前时间的long
	 * 
	 * @return
	 */
	public static long getCurrentLongTime() {
		LocalDateTime localDateTime = LocalDateTime.now();
		Instant instant = localDateTime.atZone(DEFAULT_ZONE).toInstant();
		return instant.toEpochMilli();
	}

	/**
	 * 
	 * 将指定时间格式化为指定格式
	 * 
	 * @param localDateTime
	 * @param formatter
	 * @return
	 */
	public static String getLocalDateTimeToString(LocalDateTime localDateTime, String formatter) {
		if (formatter == null || "".equals(formatter)) {
			formatter = FORMATTER_STANDARD;
		}
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatter);
		return localDateTime.format(dateTimeFormatter);
	}

	/**
	 * 
	 * 将指定时间格式化为long
	 * 
	 * @param localDateTime
	 * @param zoneId
	 * @return
	 */
	public static long getLocalDateTimeToLong(LocalDateTime localDateTime, ZoneId zoneId) {
		if (zoneId == null) {
			zoneId = DEFAULT_ZONE;
		}
		Instant instant = localDateTime.atZone(zoneId).toInstant();
		return instant.toEpochMilli();
	}

	/**
	 * 
	 * 将字符串转换为LocalDateTime
	 * 
	 * @param localDateTimeString
	 * @param formatter
	 * @return
	 */
	public static LocalDateTime getStringToLocalDateTime(String localDateTimeString, String formatter) {
		if (localDateTimeString == null || "".equals(localDateTimeString)) {
			localDateTimeString = getCurrentTime();
		}
		if (formatter == null || "".equals(formatter)) {
			formatter = FORMATTER_STANDARD;
		}
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatter);
		return LocalDateTime.parse(localDateTimeString, dateTimeFormatter);
	}

	/**
	 * 
	 * 将long转化为时间
	 * 
	 * @param localDateTimeLong
	 * @return
	 */
	public static LocalDateTime getLongToLocalDateTime(long localDateTimeLong) {
		Instant instant = Instant.ofEpochMilli(localDateTimeLong);
		return LocalDateTime.ofInstant(instant, DEFAULT_ZONE);
	}
	
	
	/**
	 * 
	 * 将long转化为指定时间格式
	 * 
	 * @param localDateTimeLong
	 * @param formatter
	 * @return
	 */
	public static String getLongToString(long localDateTimeLong, String formatter) {
		if (formatter == null || "".equals(formatter)) {
			formatter = FORMATTER_STANDARD;
		}

		Instant instant = Instant.ofEpochMilli(localDateTimeLong);

		return getLocalDateTimeToString(LocalDateTime.ofInstant(instant, DEFAULT_ZONE), formatter);
	}
	
	
	
	/**
	 * 
	 * 返回当前时间的信息：<p>
	 * 
	 *   Now ： LocalDateTime<p>
	 *   
	 *   DayOfYear  ： int<p>
	 *   DayOfMonth ： int<p>
	 *   DayOfWeek  ： String<p>
	 *   
	 *   Year  ： int<p>
	 *   Month ： String<p>
	 *   MonthValue ： int<p>
	 *    
	 *   Hour   ： int<p>
	 *   Minute ： int<p>
	 *   Second ： int<p>
	 *   Nano   ： int<p>
	 *   
	 *   LengthOfYear  ： int<p> 
	 *   LengthOfMonth ： int<p>
	 *   IsLeapYear : boolean 
	 * 
	 * @return
	 */
	public static Map<String, Object> getCurrentTimeInfo() {
		Map<String, Object> timeInfoMap = new HashMap<String,Object>();
		LocalDateTime localDateTime = LocalDateTime.now();
		
		timeInfoMap.put("Now", localDateTime);
		
		timeInfoMap.put("DayOfYear", localDateTime.getDayOfYear());
		timeInfoMap.put("DayOfMonth", localDateTime.getDayOfMonth());
		timeInfoMap.put("DayOfWeek", localDateTime.getDayOfWeek().name());
		
		timeInfoMap.put("Year", localDateTime.getYear());
		timeInfoMap.put("Month", localDateTime.getMonth().name());
		timeInfoMap.put("MonthValue", localDateTime.getMonthValue());
		
		timeInfoMap.put("Hour",  localDateTime.getHour());
		timeInfoMap.put("Minute", localDateTime.getMinute());
		timeInfoMap.put("Second", localDateTime.getSecond());
		timeInfoMap.put("Nano", localDateTime.getNano());
		
		timeInfoMap.put("LengthOfYear", localDateTime.toLocalDate().lengthOfYear());
		timeInfoMap.put("LengthOfMonth", localDateTime.toLocalDate().lengthOfMonth());
		timeInfoMap.put("IsLeapYear", localDateTime.toLocalDate().isLeapYear());
		
	
		return timeInfoMap;
	}
	
	
	/**
	 * 
	 * 返回指定时间的信息：
	 * 
	 *   Now ： LocalDateTime<p>
	 *   
	 *   DayOfYear ： int<p>
	 *   DayOfMonth ： int<p>
	 *   DayOfWeek ： String<p>
	 *   
	 *   Year ： int<p>
	 *   Month ： String<p>
	 *   MonthValue ： int<p>
	 *    
	 *   Hour ： int<p>
	 *   Minute ： int<p>
	 *   Second ： int<p>
	 *   Nano ： int<p>
	 *   
	 *   LengthOfYear ： int<p> 
	 *   LengthOfMonth ： int<p>
	 *   IsLeapYear : boolean 
	 *   
	 *   
	 * @param localDateTime
	 * @return
	 */
	public static Map<String, Object> getCustomTimeInfo(LocalDateTime localDateTime) {
		Map<String, Object> timeInfoMap = new HashMap<String,Object>();
		
		timeInfoMap.put("Now", localDateTime);
		
		timeInfoMap.put("DayOfYear", localDateTime.getDayOfYear());
		timeInfoMap.put("DayOfMonth", localDateTime.getDayOfMonth());
		timeInfoMap.put("DayOfWeek", localDateTime.getDayOfWeek().name());
		
		timeInfoMap.put("Year", localDateTime.getYear());
		timeInfoMap.put("Month", localDateTime.getMonth().name());
		timeInfoMap.put("MonthValue", localDateTime.getMonthValue());
		
		timeInfoMap.put("Hour",  localDateTime.getHour());
		timeInfoMap.put("Minute", localDateTime.getMinute());
		timeInfoMap.put("Second", localDateTime.getSecond());
		timeInfoMap.put("Nano", localDateTime.getNano());
		
		timeInfoMap.put("LengthOfYear", localDateTime.toLocalDate().lengthOfYear());
		timeInfoMap.put("LengthOfMonth", localDateTime.toLocalDate().lengthOfMonth());
		timeInfoMap.put("IsLeapYear", localDateTime.toLocalDate().isLeapYear());
		
	
		return timeInfoMap;
	}
	
	/**
	 * 获取指定时间的月和年的第一天和最后一天
	 * 
	 * 
	 * 	 FirstDayOfMonth ： LocalDateTime<p>
	 *   LastDayOfMonth  ： LocalDateTime<p>
	 *   FirstDayOfMonth ： LocalDateTime<p>
	 *   FirstDayOfMonth ： LocalDateTime<p>
	 * 
	 * @param localDateTime
	 * @return
	 */
	public static Map<String, LocalDateTime> getFirstAndLastDateTime(LocalDateTime localDateTime) {
		Map<String, LocalDateTime> map = new HashMap<String,LocalDateTime>();
		
		map.put("FirstDayOfMonth", localDateTime.with(TemporalAdjusters.firstDayOfMonth()));
		map.put("LastDayOfMonth", localDateTime.with(TemporalAdjusters.lastDayOfMonth()));
		
		map.put("FirstDayOfMonth", localDateTime.with(TemporalAdjusters.firstDayOfYear()));
		map.put("FirstDayOfMonth", localDateTime.with(TemporalAdjusters.firstDayOfYear()));
	
		return map;
	}
	
	
	/**
	 * 获取指定时间的月和年的第一天和最后一天
	 * 
	 * 
	 * 	 FirstDayOfMonth ： LocalDateTime<p>
	 *   LastDayOfMonth ： LocalDateTime<p>
	 *   FirstDayOfMonth ： LocalDateTime<p>
	 *   FirstDayOfMonth ： LocalDateTime<p>
	 * 
	 * @param localDate
	 * @return
	 */
	public static Map<String, LocalDate> getFirstAndLastDate(LocalDate localDate) {
		Map<String, LocalDate> map = new HashMap<String,LocalDate>();
		
		map.put("FirstDayOfMonth", localDate.with(TemporalAdjusters.firstDayOfMonth()));
		map.put("LastDayOfMonth", localDate.with(TemporalAdjusters.lastDayOfMonth()));
		
		map.put("FirstDayOfMonth", localDate.with(TemporalAdjusters.firstDayOfYear()));
		map.put("FirstDayOfMonth", localDate.with(TemporalAdjusters.firstDayOfYear()));
	
		return map;
	}
	
	/**
	 * 
	 * 按指定设置获取指定日期的周开始和结束时间
	 * 
	 * WeekStart ： LocalDate
	 * WeekEnd   ： LocalDate
	 * OutOfWeek : boolean  判断按照start和end找到的日期是否超过一周，如果返回false则说明传入日期和start和end可能不匹配
	 * 
	 * @param localDate
	 * @param start
	 * @param end
	 * @return
	 */
	public static Map<String, Object> getWeekDate(LocalDate localDate,DayOfWeek start,DayOfWeek end) {
		Map<String, Object> map = new HashMap<String,Object>();
		int curDayOfWeek = localDate.getDayOfWeek().getValue();
		
		if (curDayOfWeek == start.getValue()) {
			map.put("WeekStart", localDate);
		} else {
			map.put("WeekStart", localDate.with(TemporalAdjusters.previous(start)));
		}
		
		if (curDayOfWeek == end.getValue()) {
			map.put("WeekEnd", localDate);
		} else {
			map.put("WeekEnd", localDate.with(TemporalAdjusters.next(end)));
		}
		
		LocalDate startDateInclusive  = (LocalDate) map.get("WeekStart");
		LocalDate endDateExclusive  = (LocalDate) map.get("WeekEnd");
		Period period = Period.between(startDateInclusive, endDateExclusive);
		map.put("OutOfWeek", period.getDays() > 7);
		
		return map;
	}
	
	/**
	 * 
	 *按指定设置获取指定日期的周开始和结束时间
	 * 
	 * WeekStart ： LocalDateTime
	 * WeekEnd   ： LocalDateTime
	 * OutOfWeek : boolean  判断按照start和end找到的日期是否超过一周，如果返回false则说明传入日期和start和end可能不匹配
	 * 
	 * @param localDateTime
	 * @param start
	 * @param end
	 * @return
	 */
	public static Map<String, Object> getWeekDateTime(LocalDateTime localDateTime,DayOfWeek start,DayOfWeek end) {
		Map<String, Object> map = new HashMap<String,Object>();
		int curDayOfWeek = localDateTime.getDayOfWeek().getValue();
		
		if (curDayOfWeek == start.getValue()) {
			map.put("WeekStart", localDateTime);
		} else {
			map.put("WeekStart", localDateTime.with(TemporalAdjusters.previous(start)));
		}
		
		if (curDayOfWeek == end.getValue()) {
			map.put("WeekEnd", localDateTime);
		} else {
			map.put("WeekEnd", localDateTime.with(TemporalAdjusters.next(end)));
		}
	
		LocalDate startDateInclusive  = ((LocalDateTime) map.get("WeekStart")).toLocalDate();
		LocalDate endDateExclusive  = ((LocalDateTime) map.get("WeekEnd")).toLocalDate();
		Period period = Period.between(startDateInclusive, endDateExclusive);
		map.put("OutOfWeek", period.getDays() > 7);
		
		return map;
	}
	
	/**
	 * 
	 * 计算两个日期之间的天数
	 * 
	 * 
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static long getTotalDays(LocalDate startDate,LocalDate endDate) {
	
		long totalDays = ChronoUnit.DAYS.between(startDate, endDate);
		
		return totalDays;
	}
}
